Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Control point control flow #1481

Closed
wants to merge 14 commits into from
Closed

Conversation

ltratt
Copy link
Contributor

@ltratt ltratt commented Nov 29, 2024

This DRAFT PR shows two problems we have right now:

  1. The interpreter loop calling itself (i.e. for language-level calls).
  2. The interpreter loop starting tracing and finishing before tracing completes.

Both tests bork on master.

I have put in what looks like a fix yk_mt_early_return, but it leads to a segfault in Rust code in early_return.c (run the test under gdb and you'll see it crashes at a completely innocuous looking place in Rust). This might be a minimal test case of the problems that have recently plagued yklua: certainly, these two tests are things that yklua can do, and that we don't handle properly.

Thoughts welcome!

An interpreter could do something like:

```
fn f(loc: &Location) {
  for i in 0..1000 {
    control_point(loc);
    if i == hot_threshold {
      return;
    }
  }
}
```

In other words: we start tracing, then `return` before we hit the
control point in the current stack frame again.
@ltratt ltratt force-pushed the control_point_flow branch from b2b96ee to bd40930 Compare December 1, 2024 09:41
@ltratt
Copy link
Contributor Author

ltratt commented Dec 1, 2024

Here's something I can't immediately explain. Look at this trace (from recursive.c):

%0: ptr = load_ti Register(13, 8, 0, [])
%1: ptr = load_ti Register(14, 8, 0, [])
%2: ptr = load_ti Register(12, 8, 0, [])
%3: ptr = load_ti Register(3, 8, 0, [])
%4: ptr = load_ti Register(5, 8, 0, [])
%5: ptr = load_ti Register(4, 8, 0, [])
tloop_start [%0, %1, %2, %3, %4, %5]:
%7: i32 = load %2
%8: i1 = sgt %7, 2i32
guard false, %8, [0:%0_3: %0, 0:%0_4: %1, 0:%0_5: %2, 0:%0_6: %3, 0:%6_1: 1i1]
%10: ptr = lookup_global @stderr
%11: ptr = load %10
%12: i32 = load %2
%13: ptr = lookup_global @.str
%14: i32 = call @fprintf(%11, %13, %12)
%15: i32 = load %2
%16: i32 = add %15, 4294967295i32
*%2 = %16
%18: i32 = load %2
%19: i1 = sgt %18, 0i32
guard true, %19, [0:%0_3: %0, 0:%0_4: %1, 0:%0_5: %2, 0:%0_6: %3, 0:%4_1: 0i1]
%21: ptr = load %0
%22: ptr = load %1
tloop_jump [%0, %1, %2, %3, %21, %22]:

Notice that %4 and %5 are inputs, but they are not used in the trace or a guard. Even weirder at the loop point we pass different values (%21 and %22) for them, which are loads, but again those can have no effect.

Obvious question: should %4 and %5 be optimised away? Or are they doing something important and we've got a bug somewhere which isn't capturing that thing properly?

@ptersilie
Copy link
Contributor

The trace inputs are taken verbatim from the AOT stackmap. Are you sure %4 and %5 aren't optimised away by our JIT optimiser. Is this output pre-opt or post-opt?

@ltratt
Copy link
Contributor Author

ltratt commented Dec 2, 2024

From memory, you can see this in pre-opt (and post-opt, but that doesn't matter).

TransitionControlPoint::StopTracing
} else {
// ...but in a different frame.
#[cfg(target_arch = "x86_64")]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: Here we could check for StackDirection::GrowsDown (defined in regalloc.rs).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is definitely a hack but we can't use regalloc here because that backend isn't always guaranteed to be compiled in.

Probably we would need to add a top-level stack_direction function that isn't specific to a compiler backend.

[FWIW, I doubt the code in this PR should survive. It's more of an experiment to show possible problems than code I hope will be merged.]

ykrt/src/mt.rs Outdated
} => {
// We don't care if the thread tracer went wrong: we're not going to use
// its result anyway.
thread_tracer.stop().unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Following from the comment above, I guess the unwrap() should be an ok()?

@vext01
Copy link
Contributor

vext01 commented Dec 2, 2024

OK, as far as recursive interpreter loops go, I think I understand the strategy: we never permit tracing the exit of an interpreter loop. Correct?

I added a test for a scenario that I was curious about in a1e927f. Does this work as expected @ltratt? -- I think so.

@ltratt
Copy link
Contributor Author

ltratt commented Dec 2, 2024

I think so.

@ptersilie
Copy link
Contributor

I think this is mostly ready now to be turned into a proper PR. Are you taking back over @ltratt or should I be in charge of this PR now?

@ltratt
Copy link
Contributor Author

ltratt commented Dec 4, 2024

I think you or Edd should take over now.

@vext01
Copy link
Contributor

vext01 commented Dec 4, 2024

Bear with me, there, this needs a little more work.

@ltratt
Copy link
Contributor Author

ltratt commented Dec 14, 2024

Closing in favour of #1513.

@ltratt ltratt closed this Dec 16, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants